#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# This program loads images into an SQLite database, tagging 
# them with the proper metadata to allow for easy searching 
# by its counterpart - viewimages.py.
#
# VERSIONING HISTORY:
# 20140207 v0.1 - Minimal operating version; fetches an image
#                 and adds it to the database (creates one if
#                 it doesn't exist). Reads metadata info from
#                 the command-line.
#
# 20140208 v0.2 - Added an ugly UI that works - fetches file
#                 info, puts it into database.
#
# 20140208 v0.3 - Added a previewing feature that shows the
#                 user what photo will be inserted before 
#                 inserting. Needs to be tidied up, though.
#                 
#                 Constrainted the previews to a fixed size
#                 of 250x250, so it won't mess up the window
#
# 20140302 v0.4-rc1 - Removed annoying bottom bar so that the
#                     window behaves more naturally. This is
#                     fully worthy of a release now.
#
# 20140304 v1.0 - Released first sprint. Now able to insert
#                 values for the events taking place in the
#                 pictures
#
# TODO:
#
# - Due to a requirements change, we will now have to implement
#   a new metadata field: "Event." This will have to comply 
#   with the existing data, and everything else will have to be
#   tagged anew.
#
# - Enhancement proposal: query the last added photo and show 
#   information about it upon startup.
#

import os
import sys
import sqlite3 as sql
import tkFileDialog
from PIL import Image, ImageTk
from cStringIO import StringIO
import webbrowser

# Dubious imports need to be handled (not part of the standard library):
try:
    import Tkinter as tk
except ImportError:
    print "This program depends on Tkinter to work."
    print "Please install the package containing Tkinter for your distribution."
    print "Also, please note that this program is not Python3 compatible..."
    sys.exit(1)
try:
    from PIL import Image, ImageTk
except ImportError:
    print "This program depends on PIL to work"
    print "Please install the python-imaging package for your distribution."
    print "If you'd like to install it yourself, remember to enable JPEG support."
    sys.exit(1)

# Global variables for the database link.
conn = sql.connect("images.db")
cursor = conn.cursor()
if os.name == 'nt':
    pathsep = '\\'
else:
    pathsep = '/'

def sanitize():
    genesis_query = """
    CREATE TABLE IF NOT EXISTS photos (
        photo_id INTEGER PRIMARY KEY AUTOINCREMENT,
        filename VARCHAR(80),
        location VARCHAR(60),
        people TEXT,
        date_taken VARCHAR(10),
        event VARCHAR(60),
        photo_data MEDIUMBLOB NOT NULL
    );
    """
    cursor.execute(genesis_query)
    conn.commit()

def add_image(filename, location, people, date_taken, photo_data, event):
    add_query = """
        INSERT INTO photos (filename, location, people, date_taken, photo_data, event)
        VALUES
            (?,?,?,DATE(?),?, ?)
        ;
    """ 
    cursor.execute(add_query,[filename, location, people, date_taken, 
                   sql.Binary(photo_data), event])
    conn.commit()
    print("'%s' was successfully added to the database." % filename)


class AddInterface():
    '''
    This is the graphical interface that will allow users to
    easily insert images with the correct metadata in the 
    database. This interface should have a complementary pair
    to it, an interface that will search for and show the
    pictures that the users uploaded.

    There are five fields that must be filled in for optimal
    categorization:

        - Name of file (e.g. Example001.jpg)
        - Location where image was taken (e.g San Francisco)
        - People tagged in the image (e.g Me, Mom, Dad)
        - Date when image was taken (e.g. 1999-06-14)
        - Path to the image file (e.g /home/user1/Example001.jpg)
        - Event that took place in the picture (e.g Graduation)
    '''
    def gather(self):
        '''
        This method reads the fields' values, processes them 
        and clears them for another brand-new use.

        Need to develop a way to validate the input on fields
        such as date and image location!

        Update: Now saving the full path to the image, to 
        restore it as a preview.
        '''
        self.f1 = self.imagepath_field.get()
        self.f2 = self.location_field.get()
        self.f3 = self.people_field.get()
        self.f4 = self.date_field.get()
        self.f6 = self.event_field.get()
        try:
            with file(self.imagepath_field.get(), 'rb') as blob:
                self.f5 = blob.read()
            
            # process fields...
            add_image(self.f1, self.f2, self.f3, self.f4, self.f5, self.f6)
            self.statusbar.config(text = "Imagem %s foi adicionada com sucesso!" % self.f1, fg="#080")
        except IOError:
            self.statusbar.config(text = "Nao foi possivel encontrar o arquivo '%s'" % self.imagepath_field.get(), fg="#B00")

        # blank fields for new use!
        self.location_field.delete(0, tk.END)
        self.people_field.delete(0, tk.END)
        self.date_field.delete(0, tk.END)
        self.imagepath_field.delete(0, tk.END)
        self.event_field.delete(0, tk.END)

    def picker(self):
        '''
        Picks a file nice and easy through a GUI interface.
        Hey, at least this is better thatn manually typing 
        the ABSOLUTE path to every single image, isn't it?
        '''
        try:
            with tkFileDialog.askopenfile(
                parent=self.window, mode="rb",
                filetypes=[
                   ("Imagem JPEG", '*.jpg'),
                   ("Imagem PNG", '*.png'),
                   ("Imagem GIF", '*.gif')
                ],
                title="Escolha uma imagem"
            ) as img:
                # Set the path so we can later use it with the SQL script
                self.imagepath_field.delete(0, tk.END)
                self.imagepath_field.insert(0, os.path.realpath(img.name))

                # Create a nice overlay preview image!!!
                self.placeholder = Image.open(os.path.realpath(img.name))

                # Scale it down to an appropriate size if necessary:
                if self.placeholder.size[0] < 250 and self.placeholder.size[1] < 250:
                    pass
                else:
                    self.photoconstraint1 = 250.0 / self.placeholder.size[0]
                    self.photoconstraint2 = 250.0 / self.placeholder.size[1]
                    if self.photoconstraint1 > self.photoconstraint2:
                        self.photolimit = self.photoconstraint2
                    else:
                        self.photolimit = self.photoconstraint1
                    self.placeholder = self.placeholder.resize((int(self.photolimit * self.placeholder.size[0]), int(self.photolimit * self.placeholder.size[1])), Image.ANTIALIAS)

                self.preview_img = ImageTk.PhotoImage(self.placeholder)
                self.preview.config(image=self.preview_img)

        except AttributeError:
            pass # You didn't return __exit__ and "with" complained. Ignore...

    def get_last_image(self):
        '''
        This reads the last added image from the database and fetches
        information to help the user. This should be displayed in the GUI.
        '''
        print "Fetching information about last picture added..."
        cursor.execute('''
            SELECT location, date_taken, filename
            FROM photos
            WHERE photo_id = (SELECT COUNT(*) FROM photos);
            ''')
        try:
            self.results = cursor.fetchall()[0]
            self.lastpic = str(self.results[2])
            self.statusbar.config(text="Ultima foto adicionada: %s" % self.lastpic.split(pathsep)[len(self.lastpic.split(pathsep)) - 1])
            # Create a nice overlay preview image!!!
            self.placeholder = Image.open(os.path.realpath(self.lastpic))

            self.photoconstraint1 = 250.0 / self.placeholder.size[0]
            self.photoconstraint2 = 250.0 / self.placeholder.size[1]
            if self.photoconstraint1 > self.photoconstraint2:
                self.photolimit = self.photoconstraint2
            else:
                self.photolimit = self.photoconstraint1
            self.placeholder = self.placeholder.resize((int(self.photolimit * self.placeholder.size[0]), int(self.photolimit * self.placeholder.size[1])), Image.ANTIALIAS)

            self.preview_img = ImageTk.PhotoImage(self.placeholder)
            self.preview.config(image=self.preview_img)
        except IndexError:
            print "No previous picture found." 
        except IOError, e:
            print "Previous picture belonged to an older version of the app: %s" % e

    def __init__(self):
        '''
        Create the overlay widgets and pack them into the window.
        The interface is the following:
            
            - One label-field pair for text field.
            - A date-picking field for date
            - A button to choose a file from.

            - A PREVIEW for the chosen image!
            - A status bar to where all error/status messages 
              will be chanelled.
        '''
        self.window = tk.Tk()
        self.window.title("Image database indexer - VMAN")
        # Frames to hold the field-label pairs.
        self.leftpane = tk.Frame(self.window)
        self.rightpane = tk.Frame(self.window, width=250, height=250)
        self.rightpane.pack_propagate(0)
        self.location = tk.Frame(self.leftpane)
        self.people = tk.Frame(self.leftpane)
        self.date = tk.Frame(self.leftpane)
        self.imagepath = tk.Frame(self.leftpane)
        self.event = tk.Frame(self.leftpane)

        # Menubar! ZOMG
        self.menubar = tk.Menu(self.window)
        self.filemenu = tk.Menu(self.menubar, tearoff=0)
        self.filemenu.add_command(
            label="Sobre",
            command=lambda url="http://pilimage.googlecode.com/": webbrowser.open(url)
        )
        self.filemenu.add_command(label="Sair", command=self.window.quit)
        self.menubar.add_cascade(label="Arquivo", menu=self.filemenu)
        self.window.config(menu=self.menubar)

        # Fields
        self.location_field = tk.Entry(self.location, bg="#DDDDDD", relief="flat",
                                       highlightcolor="#069D00")
        self.people_field = tk.Entry(self.people, bg="#DDDDDD", relief="flat",
                                     highlightcolor="#069D00")
        self.date_field = tk.Entry(self.date, bg="#DDDDDD", relief="flat",
                                   highlightcolor="#069D00")
        self.imagepath_field = tk.Entry(self.imagepath, bg="#DDDDDD", relief="flat",
                                        highlightcolor="#069D00")
        self.text_content = tk.Label(self.window, text="""\
Adicione as informações necessárias para\ncadastrar as imagens e clique Cadastrar""",
                                     font="TkDefaultFont 12")
        self.gather_info = tk.Button(self.leftpane, text="Cadastrar", command=self.gather)
        self.choose_file = tk.Button(self.imagepath, text="Escolher arquivo",
                                     command = self.picker)
        
        self.event_field = tk.Entry(self.event, bg="#DDDDDD", relief="flat",
                                   highlightcolor="#069D00")

        # Labels
        self.location_label = tk.Label(self.location, text="Lugar da foto:")
        self.people_label = tk.Label(self.people, text="Pessoas na foto:")
        self.date_label = tk.Label(self.date, text="Data tirada:")
        self.event_label = tk.Label(self.event, text="Evento da foto")
        self.imagepath_label = tk.Label(self.imagepath, text="Arquivo:")
        self.statusbar = tk.Label(self.leftpane,
                                  font = "tkDefaultFont 12",
                                  text = "Banco de dados pronto")
        
        # This bit refers to the image preview feature! Holy shit!
        # First, create a placeholder:
        self.preview = tk.Label(self.rightpane, width=250, height=250)

        # packing begins
        self.text_content.pack(padx=5, pady=10)
        self.leftpane.pack(side='left')
        self.rightpane.pack(side='right')
        self.imagepath.pack(expand=1)
        self.location.pack(expand=1)
        self.people.pack(expand=1)
        self.date.pack(expand=1)
        self.event.pack(expand=1)
        
        self.imagepath_label.pack(side='left')
        self.imagepath_field.pack(side='left', expand=1, fill='x', pady=5)
        self.choose_file.pack(side='left', padx=5, pady=5)
        self.location_label.pack(side='left')
        self.location_field.pack(side='left', expand=1, fill='x', pady=5)
        self.people_label.pack(side='left')
        self.people_field.pack(side='left', expand=1, fill='x', pady=5)
        self.date_label.pack(side='left')
        self.date_field.pack(side='left', expand=1, fill='x', pady=5)
        self.event_label.pack(side='left')
        self.event_field.pack(side='left', expand=1, fill='x', pady=5)

        self.gather_info.pack(side="bottom")
        self.preview.pack(padx=10, pady=10)
        self.statusbar.pack(side='bottom', pady=10)

    def main(self):
        self.window.mainloop()


if __name__ == "__main__":
    sanitize()
    print("Fresh new database created!")
    print("Getting ready to add file.")
    app = AddInterface()
    app.get_last_image()
    app.main()

